/**    
  * Copyright (C) 2006, Laboratorio di Valutazione delle Prestazioni - Politecnico di Milano

  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.

  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.

  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */

package jmt.engine.NodeSections;

import java.util.LinkedList;
import java.util.ListIterator;

import jmt.engine.NetStrategies.ServiceStrategy;
import jmt.engine.QueueNet.Job;
import jmt.engine.QueueNet.JobClass;
import jmt.engine.QueueNet.NetEvent;
import jmt.engine.QueueNet.NetMessage;
import jmt.engine.random.Parameter;

/**
 * This class implements a job source (generator): a job is created every
 * <i>t</i> simulation time units using a statisitcal distribution according
 * to its class.
 * @author Francesco Radaelli, Federico Granata
 *
 */
public class RandomSource extends InputSection {

	public static int PARAMETERS_EXCEPTION = 0x0001;

	/** Service distribution: one for each job class. */
	//	protected Distribution distributions[];
	/** Distribution parameters: one for each job class. */
	protected Parameter parameters[];

	private boolean coolStart;//when is true the waitingjobs queue is void

	private LinkedList<Job> waitingJobs; //TODO: se riusciamo a convertire in job info list è meglio

	private ServiceStrategy[] strategy;

	/**
	 * Creates a new instance of inputSection.
	 * strategy[i] = null, if the i-th class is closed.
	 */
	public RandomSource(ServiceStrategy[] strategy) {
		super();
		this.strategy = strategy;
		waitingJobs = new LinkedList<Job>();
		coolStart = true;

		//NEW
		//@author Stefano Omini
		//log = NetSystem.getLog();
		//end NEW
	}

	@Override
	protected int process(NetMessage message) throws jmt.common.exception.NetException {
		Job job;
		double delay;
		int c;
		switch (message.getEvent()) {

			case NetEvent.EVENT_START:

				//case EVENT_START:
				//the random source creates all the jobs requested by each class.
				//for each job created, it sends to itself a message whose delay is the time of
				//departure of the job, calculated using the strategy of the corresponding class

				//log.write(NetLog.LEVEL_RELEASE, null, this, NetLog.EVENT_START);

				ListIterator<JobClass> jobClasses = getJobClasses().listIterator();
				JobClass jobClass;

				while (jobClasses.hasNext()) {
					jobClass = jobClasses.next();

					//NEW
					//@author Stefano Omini
					if (jobClass.getType() == JobClass.CLOSED_CLASS) {
						//closed class: no arrivals
						continue;
					}
					//end NEW

					c = jobClass.getId();

					// Calculates the delay of departure (1/lambda)
					if (strategy[c] != null) {
						job = new Job(jobClass);
						delay = strategy[c].wait(this);
						sendMe(job, delay);
						//log.write(NetLog.LEVEL_DEBUG, job, this, NetLog.JOB_CREATED);
					}

				}
				break;

			case NetEvent.EVENT_ACK:

				//case EVENT_ACK:
				//if there are waiting jobs, takes the first, set its bornTime and
				//forwards it to the service section.
				//then it creates a new job and sends to itself a message whose delay is the time of
				//departure of that job.
				//otherwise, if there are no waiting jobs, sets coolstart=true

				if (waitingJobs.size() != 0) {
					job = waitingJobs.removeFirst();
					c = job.getJobClass().getId();
					job.born();

					//NEW
					//@author Stefano Omini, Bertoli Marco

					// in RandomSource the job is created (--> SystemEnteringTime is initialized)
					// but then is delayed as long as the random interarrival time ("delay")
					//
					// to compute system response time, the job starting time must be
					// reset (otherwise it will correspond to the creation time and not to the
					// leaving time, which is "delay" seconds after)

					// Signals to global jobInfoList new added job
					this.getOwnerNode().getQueueNet().getJobInfoList().addJob(job);

					//end NEW

					sendForward(job, 0.0);

					//log.write(NetLog.LEVEL_DEBUG, job, this, NetLog.JOB_OUT);

					// Create a new job and send it to me delayed
					job = new Job(getJobClasses().get(c));
					delay = strategy[c].wait(this);
					sendMe(job, delay);

					//log.write(NetLog.LEVEL_DEBUG, job, this, NetLog.JOB_CREATED);

				} else {
					coolStart = true;
				}
				break;

			case NetEvent.EVENT_JOB:

				//case EVENT_JOB
				//if coolStart=false adds the job to the list of waiting jobs.
				//
				//if coolStart=true (no waiting jobs)  the job is forwarded, an ack message
				//is sent to the source of the message and a new job is created (the random source
				//sends to itself a message, whose delay is the time of departure of the new job).

				//log.write(NetLog.LEVEL_DEBUG, message.getJob(), this, NetLog.JOB_IN);

				// Gets the job from the message
				job = message.getJob();

				if (coolStart) {
					// Gets the class of the job
					c = job.getJobClass().getId();

					//no control is made on the number of jobs created
					//it's an open class
					job.born();

					//NEW
					//@author Stefano Omini, Bertoli Marco

					// in RandomSource the job is created (--> SystemEnteringTime is initialized)
					// but then is delayed as long as the random interarrival time ("delay")
					//
					// to compute system response time, the job starting time must be
					// reset (otherwise it will correspond to the creation time and not to the
					// leaving time, which is "delay" seconds after)

					// Signals to global jobInfoList new added job
					this.getOwnerNode().getQueueNet().getJobInfoList().addJob(job);

					//end NEW

					sendForward(job, 0.0);
					send(NetEvent.EVENT_ACK, job, 0.0, message.getSourceSection(), message.getSource());

					//log.write(NetLog.LEVEL_DEBUG, job, this, NetLog.JOB_OUT);

					job = new Job(job.getJobClass());
					delay = strategy[c].wait(this);
					sendMe(job, delay);

					// Sets coolStart to false, next job should wait ack
					coolStart = false;

					//log.write(NetLog.LEVEL_DEBUG, job, this, NetLog.JOB_CREATED);
					return MSG_PROCESSED;

				} else {
					//coolStart is false: there are waiting jobs. Add the received job.
					waitingJobs.add(job);
				}
				break;

			default:
				return MSG_NOT_PROCESSED;
		}
		return MSG_PROCESSED;
	}
}
